﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace Shell.PocoDot.Configurations
{
    /// <summary>
    /// 默认的 PocoDot 之间的关系的注册处
    /// </summary>
    public class DefaultRelationDotRegistrar : IRelationRegistrar
    {
        /// <summary>
        /// 构遭一个新的实例
        /// </summary>
        public DefaultRelationDotRegistrar()
        {
            this.Configs = new List<RelationConfiguration>();
        }

        /// <summary>
        /// 获取默认的PocoDot 之间的关系的名称的格式
        /// </summary>
        protected virtual string DefaultNameFormat
        {
            get { return "{0}_{1}"; }
        }

        /// <summary>
        /// 获取默认的持久层提供器的固定名称
        /// </summary>
        protected virtual string DefaultProviderInvariantName
        {
            get { return "System.Data.SqlClient"; }
        }

        /// <summary>
        /// 获取默认的持久层提供器的清单另牌
        /// </summary>
        protected virtual string DefaultProviderManifestToken
        {
            get { return "2008"; }
        }

        /// <summary>
        /// 获取所有已注册的 PocoDot 的关系的配置信息
        /// </summary>
        protected List<RelationConfiguration> Configs { get; private set; }

        /// <summary>
        /// 获取所有已注册的 PocoDot 的关系的配置信息
        /// </summary>
        public virtual IEnumerable<RelationConfiguration> Configurations
        {
            get { return Configs; }
        }

        /// <summary>
        /// 注册特定的 PocoDot 之间的关系的配置信息
        /// </summary>
        /// <param name="config">PocoDot 之间的关系的配置信息</param>
        public virtual void Register(RelationConfiguration config)
        {
            lock (this.Configs)
            {
                this.CheckRelatedPocoDotConfiguration(config);
                this.CheckProviderConfiguration(config.ProviderConfiguration);
                Configs.Add(config);
            }
        }

        /// <summary>
        /// 检查持久层提供器的配置信息是否合法
        /// </summary>
        /// <param name="pconfig">持久层提供器的配置信息</param>
        /// <remarks>此方法将直接抛出异常, 无需返回结果</remarks>
        protected virtual void CheckProviderConfiguration(ProviderConfiguration pconfig)
        {
            ConfigurationHelper.Check(pconfig,
                this.DefaultProviderInvariantName, this.DefaultProviderManifestToken);
        }

        /// <summary>
        /// 检查 PocoDot 的关系的配置信息是否合法
        /// </summary>
        /// <param name="rconfig">PocoDot 的关系的配置信息</param>
        /// <remarks>此方法将直接抛出异常, 无需返回结果</remarks>
        protected virtual void CheckRelatedPocoDotConfiguration(RelationConfiguration rconfig)
        {
            if (rconfig == null)
            {
                throw new ArgumentNullException("rconfig");
            }
            if (rconfig.ProviderConfiguration == null)
            {
                throw new ArgumentNullException("rconfig.Provider");
            }
            var mainType = rconfig.MainPocoDot;
            var relatedType = rconfig.RelatedPocoDot;
            if (mainType == null)
            {
                throw new ArgumentNullException("rconfig.MainPocoDot");
            }
            if (relatedType == null)
            {
                throw new ArgumentNullException("rconfig.RelatedPocoDot");
            }
            if (this.Configs.Any(c => (c.MainPocoDot == mainType && c.RelatedPocoDot == relatedType)
                || (c.MainPocoDot == relatedType && c.RelatedPocoDot == mainType)))
            {
                throw new InvalidOperationException(SR.Get("Error.DuplicatedRelationshipConfigurationFound", mainType.Name, relatedType.Name));
            }
            var pocoregistar = PocoDotRegistrar.Registrar;
            if (pocoregistar.Get(mainType) == null)
            {
                throw new InvalidOperationException(SR.Get("Error.PocoConfigurationNotExist", mainType.Name));
            }
            if (pocoregistar.Get(relatedType) == null)
            {
                throw new InvalidOperationException(SR.Get("Error.PocoConfigurationNotExist", relatedType.Name));
            }
        }
    }
}
